<template>
  <el-select
    v-model="modelValue"
    clearable
    filterable
    :filter-method="filterOptions"
    :loading="loading"
    :placeholder="placeholder"
    @change="onChange"
  >
    <div
      v-infinite-scroll="loadOptions"
      :infinite-scroll-disabled="loading"
      style="overflow-y: hidden"
    >
      <el-option
        v-for="item in loadedOptions"
        :key="item[valueKey]"
        :label="item[labelKey]"
        :value="item[valueKey]"
      />
    </div>
  </el-select>
</template>

<script setup lang="ts">
import { useVModel } from '@vueuse/core'
// import { withDefaults, defineOptions, defineEmits, defineProps } from 'vue'
import { ElSelect, ElOption } from 'element-plus'

defineOptions({ name: 'InfiniteSelect' })

const props = withDefaults(
  defineProps<{
    options?: any[]
    labelKey?: string
    valueKey?: string
    api?: Function
    modelValue?: any
    placeholder?: string
    loadBySelf?: boolean
  }>(),
  {
    options: () => [] as any[],
    labelKey: 'label',
    valueKey: 'value',
    modelValue: undefined,
    placeholder: '请选择',
    loadBySelf: false
  }
)

const emit = defineEmits(['update:modelValue', 'change'])
const modelValue = useVModel(props, 'modelValue', emit)

let pageIndex = 0
const pageSize = 30
const loading = ref(false)
const loadedOptions = ref([] as any[])
const query = ref('')

const loadData = async () => {
  const begin = pageIndex * pageSize
  const end = begin + pageSize
  let list = await props.api!({ pageIndex, pageSize, query: query.value, selected: modelValue.value })
  if(query.value){
    list = list.filter((item) => item[props.labelKey].includes(query.value))
  }
  const result = list.slice(begin, end)
  if(modelValue.value && !loadedOptions.value.some((item) => item[props.valueKey] == modelValue.value)){
    const item = list.find((item) => item[props.valueKey] == modelValue.value)
    if(item){
      result.unshift(item)
    }
  }else if(modelValue.value && list.some(item=> item[props.valueKey] == modelValue.value) && loadedOptions.value[0][props.valueKey] == modelValue.value){
    loadedOptions.value.shift()
  }
  return result
}

const loadOptions = async () => {
  loading.value = true
  if (props.api) {
    const list = props.loadBySelf ? await props.api({ pageIndex, pageSize, query: query.value, selected: modelValue.value }): await loadData()
    if (list.length > 0) {
      loadedOptions.value = [...loadedOptions.value, ...list]
      pageIndex++
    }
  } else {
    loadedOptions.value = props.options || []
  }
  loading.value = false
}

const filterOptions = async (search: string) => {
  if(query.value === search && loadedOptions.value.length){
    return loadedOptions.value
  }
  query.value = search
  if (props.api) {
    loadedOptions.value = []
    pageIndex = 0
    const list = props.loadBySelf ? await props.api({ pageIndex, pageSize, query: search, selected: modelValue.value  }): await loadData()
    pageIndex++
    loadedOptions.value = list
    return list
  } else {
    return loadedOptions.value.filter((item) => item[props.labelKey].includes(search))
  }
}

watch(()=>props.modelValue, async (val)=>{
  if(props.api && val && !loadedOptions.value.some(item=>item[props.valueKey] === val)){
    pageIndex = 0
    const list = props.loadBySelf ? await props.api({ pageIndex, pageSize: 30, query: '', selected: modelValue.value  }): await loadData()
    pageIndex++
    loadedOptions.value = list
  }
}, { immediate: true })

const onChange = (val: any) => {
//  modelValue.value = val
 // emit('update:modelValue', val)
  const item = loadedOptions.value.find(item=>item[props.valueKey] === val)
  emit('change', item)
}
</script>
